//
//   Copyright 2011 Gynvael Coldwind & Mateusz "j00ru" Jurczyk
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
#include <math.h>
#include "gynvael.h"
#include "global.h"
#include "j00ru.h"

FONT_DESC *fonts[10];

FONT_DESC *
fnt_CreateFont(const char *fname, const char *charset, int ch_w, int ch_h)
{
  // Load image.
  SDL_Surface *img;

  img = IMG_Load(fname);
  if(!img)
    fprintf(stderr, "fnt_CreateFont: File %s not found", fname);

  // Convert to 32 bits.
  SDL_PixelFormat fmt = {
    NULL, 32, 4, 
    0, 0, 0, 0,
    0, 8, 16, 24,
    0xff, 0xff00, 0xff0000, 0xff000000,
    0,
    0xff
  };
  SDL_Surface *nimg = SDL_ConvertSurface(img, &fmt, SDL_SWSURFACE);
  SDL_FreeSurface(img);

  // Transparentize.
  int w = nimg->w, h = nimg->h;

  int px_count = 0, tr_count = 0;

  uint32_t *p = (uint32_t*)nimg->pixels;
  for(int j = 0; j < h; j++)
  {
    for(int i = 0; i < w; i++, p++, px_count++)
    {
      uint32_t v = *p;
      if((v & 0xffffff) == 0)
      {
        v = 0; // Alpha + color to 0
        tr_count++;
      }
      else
      {
        v |= 0xff000000;
      }

      *p = v;
    }
  }

  // So far so good, create the descriptor
  FONT_DESC *fnt = new FONT_DESC;
  fnt->font = nimg;
  fnt->ch_w = ch_w;
  fnt->ch_h = ch_h;
  fnt->ch_per_line = nimg->w / ch_w;

  if(nimg->w % ch_w != 0)
  {
    printf("fnt_CreateFont: Warning: font bitmap width MUST be dividable by char width\n");
  }

  // Map
  for(int i = 0; charset[i]; i++)
  {
    fnt->ch_to_idx[int(charset[i])] = i;

    // Lower to upper, upper to lower.
    if(charset[i] >= 'A' && charset[i] <= 'Z')
      fnt->ch_to_idx[int(charset[i] + 'a' - 'A')] = i;

    if(charset[i] >= 'a' && charset[i] <= 'z')
      fnt->ch_to_idx[int(charset[i] + 'A' - 'a')] = i;    
  }

  // Done.
  printf("Font \"%s\" loaded (char is %ix%i, %i chars per line)\n",
      fname, ch_w, ch_h, fnt->ch_per_line);

  return fnt; 
}

unsigned int 
fnt_TextToTexture(FONT_DESC *fnt, int w, int h, const char *fmt, ...)
{
  // Render text.
  char final_text[4096]; // This is enough
  va_list arg_ptr;

  va_start(arg_ptr, fmt);
  vsnprintf(final_text, 4096, fmt, arg_ptr);
  va_end(arg_ptr);

  // Check if this is already rendered.
  char id_text[4096 + 32];
  sprintf(id_text, "%i|%i|%s", w, h, final_text);
  std::string id(id_text);

  std::map<std::string,unsigned int>::iterator cache_iter = fnt->text_to_texid.find(id);
  if(cache_iter != fnt->text_to_texid.end())
  {
    // Yep, cached.
    return cache_iter->second;
  }

  // Create new SDL surface.
  SDL_PixelFormat pixel_fmt = {
    NULL, 32, 4, 
    0, 0, 0, 0,
    0, 8, 16, 24,
    0xff, 0xff00, 0xff0000, 0xff000000,
    0,
    0xff
  };

  SDL_Surface *dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
      w,h, 32, 
      pixel_fmt.Rmask, pixel_fmt.Gmask, pixel_fmt.Bmask, pixel_fmt.Amask);

  // Render text into dst
  uint32_t *src_bmp = (uint32_t*)fnt->font->pixels;
  uint32_t *dst_bmp = (uint32_t*)dst->pixels;
  int ch_w = fnt->ch_w;
  int ch_h = fnt->ch_h;
  int fnt_w = fnt->font->w;
  int per_line = w / ch_w;
  int line_cnt = h / ch_h;
  for(int i = 0, dst_x = 0, dst_y = 0;
      final_text[i];
      i++)
  {
    // Next line?
    if(dst_x >= per_line || final_text[i] == '\n')
    {
      dst_x = 0; dst_y++;
      continue;
    }

    if(dst_y >= line_cnt)
      break;

    // Space?
    if(final_text[i] == ' ')
    {
      dst_x++;
      continue;
    }  

    // Actually render something. Perhaps.
    int idx = fnt->ch_to_idx[final_text[i]];

    int bmp_src_x = (idx % fnt->ch_per_line) * ch_w;
    int bmp_src_y = (idx / fnt->ch_per_line) * ch_h;

    int bmp_dst_x = dst_x * ch_w;
    int bmp_dst_y = dst_y * ch_h;

    // Blit.
    for(int n = 0; n < ch_h; n++)
    {
      uint32_t *psrc = &src_bmp[(n + bmp_src_y) * fnt_w + bmp_src_x];
      uint32_t *pdst = &dst_bmp[(n + bmp_dst_y) * w + bmp_dst_x];
      memcpy(pdst, psrc, ch_w * 4);
    }

    dst_x++;
  }

  unsigned int texture;
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
      
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h,
    0, GL_RGBA, GL_UNSIGNED_BYTE, dst->pixels);

  SDL_FreeSurface(dst);

  // Add to cache.
  fnt->text_to_texid[id] = texture;
  /*printf("fnt_TextToTexture: cached %s\n",
      id_text);*/

  // Done.
  return texture;
}

